using System;
using System.Collections.Generic;
using System.Text;

namespace TcKs.MSOffice.WrapperAssembliesCodeGenerator {
	/// <summary>
	/// Interface for classes providing source code building ability.
	/// </summary>
	public interface ISourceCodeBuilder {
		/// <summary>
		/// String used for indentation.
		/// </summary>
		string IndentationText { get;set;}
		/// <summary>
		/// Size of indentation.
		/// </summary>
		byte IndentationSize { get;set;}

		/// <summary>
		/// Appends specified text.
		/// </summary>
		/// <param name="text">Text to append.</param>
		void Add( string text );
		/// <summary>
		/// Append specified text and new line.
		/// </summary>
		/// <param name="text">Text to append.</param>
		void Line( string text );
		/// <summary>
		/// Append new line.
		/// </summary>
		void Line();
		/// <summary>
		/// Add one indent.
		/// </summary>
		void Indent();
		/// <summary>
		/// Remove one indent.
		/// </summary>
		void Unindent();
		/// <summary>
		/// Add one indent and returs object which removes one indent after the "Dispose" method is called.
		/// This method is usefull in "using( ... ) { ... }" block.
		/// </summary>
		/// <returns></returns>
		IDisposable IndentBlock();
		/// <summary>
		/// Flushs buffer.
		/// </summary>
		void Flush();
	}

	/// <summary>
	/// Base class for source code builders.
	/// </summary>
	/// <typeparam name="T_CODE_BUILDER">Final type of source code builder.</typeparam>
	public abstract class SourceCodeBuilderBase<T_CODE_BUILDER> : ISourceCodeBuilder where T_CODE_BUILDER : SourceCodeBuilderBase<T_CODE_BUILDER> {
		private System.IO.TextWriter writer;

		//public SourceCodeBuilderBase() : this( new StringBuilder() ) { }
		//public SourceCodeBuilderBase( int capacity ) : this( new StringBuilder( capacity ) ) { }
		//public SourceCodeBuilderBase( StringBuilder code ) {
		//    if ( null == code ) { throw new ArgumentNullException( "code" ); }

		//    this.code = code;
		//}
		public SourceCodeBuilderBase( System.IO.TextWriter writer ) {
			if ( null == writer ) { throw new ArgumentNullException( "writer" ); }

			this.writer = writer;
		}
		public SourceCodeBuilderBase( StringBuilder code ) : this( new System.IO.StringWriter( code ) ) { }

		private bool isNewLine;
		protected void AddIndentationTexts() {
			int num = this.IndentationSize;
			if ( num > 0 ) {
				string strIndent = this.IndentationText;
				if ( false == string.IsNullOrEmpty( strIndent ) ) {
					for ( int i = 0; i < num; i++ ) {
						//this.code.Append( strIndent );
						writer.Write( strIndent );
					}
				}
			}
		}
		protected void IfRequiredAddIndentationTexts() {
			if ( this.isNewLine ) {
				this.AddIndentationTexts();
				this.isNewLine = false;
			}
		}
		public T_CODE_BUILDER Add( string text ) {
			this.IfRequiredAddIndentationTexts();
			//this.code.Append( text );
			this.writer.Write( text );
			return (T_CODE_BUILDER)this;
		}
		public T_CODE_BUILDER Line( string text ) {
			this.Add( text );
			return this.Line();
		}
		public T_CODE_BUILDER Line() {
			//this.code.AppendLine();
			this.IfRequiredAddIndentationTexts();
			this.writer.WriteLine();
			this.isNewLine = true;
			return (T_CODE_BUILDER)this;
		}
		protected virtual string NormalizeText( string text ) { return text; }
		public T_CODE_BUILDER _( string text ) {
			string normalizedText = this.NormalizeText( text );
			return this.Add( normalizedText );
		}
		public T_CODE_BUILDER _( Type type ) { return this.Add( "Global." ).Add( type.FullName ); }
		public T_CODE_BUILDER _<T>() { return this._( typeof( T ) ); }
		public T_CODE_BUILDER _IF( bool condition, string text ) { if ( condition ) { this._( text ); } return (T_CODE_BUILDER)this; }
		public T_CODE_BUILDER _IF( bool condition, Type type ) { if ( condition ) { this._( type ); } return (T_CODE_BUILDER)this; }
		public T_CODE_BUILDER _IF<T>( bool condition ) { return this._IF( condition, typeof( Type ) ); }

		private string indentationText = "\t";
		public string IndentationText {
			get { return this.indentationText; }
			set { this.indentationText = value; }
		}

		private byte indentationSize;
		public byte IndentationSize {
			get { return this.indentationSize; }
			set { this.indentationSize = value; }
		}

		public void Indent() { if ( this.indentationSize < byte.MaxValue ) { this.indentationSize++; } }
		public void Unindent() { if ( this.indentationSize > byte.MinValue ) { this.indentationSize--; } }
		public IndentToken IndentBlock() {
			this.Indent();
			return new IndentToken( this );
		}

		public void Flush() {
			this.writer.Flush();
		}

		//public override string ToString() {
		//    //return base.ToString();
		//    return this.code.ToString();
		//}

		public struct IndentToken : IDisposable {
			private ISourceCodeBuilder codeBuilder;
			public IndentToken( ISourceCodeBuilder codeBuilder ) {
				this.codeBuilder = codeBuilder;
			}

			public void ReleaseIndent() {
				ISourceCodeBuilder codeBuilder = this.codeBuilder;
				this.codeBuilder = null;
				if ( null != codeBuilder ) {
					codeBuilder.Unindent();
				}
			}

			#region IDisposable Members

			void IDisposable.Dispose() {
				this.ReleaseIndent();
			}

			#endregion
		}

		#region ISourceCodeBuilder Members

		string ISourceCodeBuilder.IndentationText {
			get { return this.IndentationText; }
			set { this.IndentationText = value; }
		}

		byte ISourceCodeBuilder.IndentationSize {
			get { return this.IndentationSize; }
			set { this.indentationSize = value; }
		}

		void ISourceCodeBuilder.Add( string text ) {
			this.Add( text );
		}

		void ISourceCodeBuilder.Line( string text ) {
			this.Line( text );
		}

		void ISourceCodeBuilder.Line() {
			this.Line();
		}

		void ISourceCodeBuilder.Indent() {
			this.Indent();
		}

		void ISourceCodeBuilder.Unindent() {
			this.Unindent();
		}

		IDisposable ISourceCodeBuilder.IndentBlock() {
			return this.IndentBlock();
		}

		void ISourceCodeBuilder.Flush() {
			this.Flush();
		}

		#endregion
	}
	public sealed class SourceCodeBuilder : SourceCodeBuilderBase<SourceCodeBuilder> {
		//public SourceCodeBuilder() : base() { }
		//public SourceCodeBuilder( int capacity ) : base( capacity ) { }
		public SourceCodeBuilder( System.IO.TextWriter writer ) : base( writer ) { }
		public SourceCodeBuilder( StringBuilder code ) : base( code ) { }
	}
}
